grammar JKlasy;

options {
	language = Java;
  	k = *;
  	output = AST;
}

tokens {
	GREQ 		= '>=';
	LEQ 		= '<=';
	ARROW 		= '=>';
	ASSIGN 		= ':=';
	NOTEQ   	= '!=';
	PLUS 		= '+' ;
	MINUS		= '-' ;
	MULT		= '*' ;
	LPAREN  	= '(' ;
	RPAREN  	= ')' ;
	LCURLY  	= '{' ;
	RCURLY  	= '}' ;
	LSQUARE 	= '[' ;
	RSQUARE 	= ']' ;
	SEMI    	= ';' ;
	COMA		= ',';
	DOT			= '.';
	PROGRAM 	= 'program';
	CLASS		= 'class';
	IMPLEMENTATION 	= 'implementation';
	BEGIN 		= 'begin';
	END			= 'end';
	IF 			= 'if';
	THEN 		= 'then';
	ELSE		= 'else';
	WHILE		= 'while';
	LOOP 		= 'loop';
	RETURN		= 'return';
	INTT		= 'integer';
	BOOLT		= 'boolean';
	CHART		= 'char';
	STRINGT		= 'string';
	REALT		= 'real';
	OPERATION 	= 'operation';
	IS			= 'is';
	INPUT		= 'input';
	OUTPUT		= 'output';
	DIV			= 'div';
	MOD			= 'mod';
	NOT			= 'not';
	AND			= 'and';
	OR			= 'or';
	TRUE		= 'true';
	FALSE		= 'false';	
	ARRAY 		= 'array';
	OF			= 'of';
	NULL		= 'null';
	PRIVATE		= 'private';
	PUBLIC 		= 'public';
	
	ARRAY;
	MAINPART;
	CLASSDEFINITION;
	VARIABLEDECLARATION;
	DECLARATIONTYPE;
	PARAMETERTYPE;
	OPERATIONDESCRIPTOR;
	OPERATIONALGORITHM;
	ARGUMENTS;
	VARIABLES;
	STATEMENTS;
	IFSTATEMENT;
	CALLSTATEMENT;
	CALL;
	SIMPLECALL;
	ASSIGNSTATEMENT;
	WHILESTATEMENT;
	INPUTSTATEMENT;
	OUTPUTSTATEMENT;
	EXPRESSION;
	RESULT;
	INDEX;
	CAST;
	VAREXPR;
	EXTENDEDID;
}
@members {
    private List<String> errors = new LinkedList<String>();
    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
        String hdr = getErrorHeader(e);
        String msg = getErrorMessage(e, tokenNames);
        errors.add(hdr + " " + msg);
    }
    public List<String> getErrors() {
        return errors;
    }
}

@parser::header {
package compiler.parser;
import java.util.LinkedList;
}

@lexer::header {
package compiler.lexer;
}


/*------------------------------------------------------------------
 * PARSER RULES
 *------------------------------------------------------------------*/

program:
	main_part (class_definition)*	
	
	->
		^(PROGRAM main_part class_definition*) 
	;

main_part:
	PROGRAM
		(variable_declaration)* 
	BEGIN
	  statements 
	END SEMI	
	
	-> 
		^(MAINPART variable_declaration* statements)
	;
	
class_definition:    
	  ID COLON CLASS 
		  (operation_descriptor SEMI)* 
	  IMPLEMENTATION
	  	(fields_declaration)
	  	(operation_algorithm)*
	  END SEMI
	  ->
	  	^(CLASSDEFINITION ID operation_descriptor* 
	  		fields_declaration? operation_algorithm*)
	;
	
fields_declaration:
    variable_declaration*
    ;	
	
variable_declaration:    
    ID (COMA ID)* COLON d=declaration_type SEMI
    ->
    	^(VARIABLEDECLARATION ($d ID)+)
	;

declaration_type: 
	(ARRAY LSQUARE INTNUMBER RSQUARE OF)? simple_type
	->
		^(DECLARATIONTYPE simple_type ^('array' OF INTNUMBER)?)
	;

parameter_type:
	(ARRAY LSQUARE RSQUARE OF)? simple_type
	->
		^(PARAMETERTYPE simple_type ^('array' OF)?)
	;
	
simple_type:
      INTT
	| BOOLT
	| STRINGT
	| REALT
	| CHART
	| ID
	;

return_type:
	  (ARROW parameter_type)? -> ^(RESULT parameter_type?)
	;

modifier:
	  PRIVATE
	| PUBLIC
	;

operation_descriptor:    	
    modifier? designator COLON 
    		LPAREN 
    			(parameter_type (COMA parameter_type)* )? 
    		RPAREN
    	return_type
    ->
    	^(OPERATIONDESCRIPTOR modifier? designator parameter_type* return_type)
	;
	
designator:
	  ID
	| relational_operator
	| additive_operator
	| multiplicative_operator
	;

operation_algorithm:
    OPERATION COLON designator
    	LPAREN 
    		(ID (COMA ID)* )? 
    	RPAREN IS
		  (variable_declaration)*
	  BEGIN
	  	statements
	  	(RETURN expression SEMI)?
	  END SEMI
	  ->
	  	^(OPERATIONALGORITHM designator ^(ARGUMENTS (ID+)?) 
	  	  ^(VARIABLES variable_declaration*) statements ^(RETURN expression)?)
	;

statements:   
   (statement)*
   ->
   		^(STATEMENTS statement*)
	;

statement:
      assign_statement SEMI!
	| if_statement 
	| while_statement SEMI!
	| input_statement SEMI!
	| output_statement SEMI!
	| call_statement SEMI!
	;

if_statement 
	:
	  IF LPAREN expression RPAREN
	  	c1=conditional_option
	  ( 
	  	  ELSE c2=conditional_option -> ^(IFSTATEMENT expression $c1 $c2)
	  	| 							 -> ^(IFSTATEMENT expression $c1)
	  )
	;
	
conditional_option:
	  statement
	| BEGIN statements END SEMI -> statements
	;
	
assign_statement:
	  extended_id (LSQUARE e1=expression RSQUARE)? ASSIGN e2=expression
	  ->
	  	^(ASSIGNSTATEMENT extended_id (INDEX $e1)? $e2)  	
	;
	
call_statement:
	  call
	  ->
	  	^(CALLSTATEMENT call)
	;

call:
	  extended_id LPAREN (expression (COMA expression)*)? RPAREN
	  ->
	  	^(CALL extended_id expression*)
	  ;
	
while_statement:	  
	  WHILE LPAREN expression RPAREN LOOP
			statements
	  END LOOP
	  ->
	  	^(WHILESTATEMENT expression statements)
	  ;

extended_id:
	  ID (DOT ID)* 
	  ->
	  	^(EXTENDEDID ID+)
	  ;
 
input_statement:	 	
	  INPUT extended_id (COMA extended_id)*
	  ->
	  	^(INPUTSTATEMENT extended_id+)	
	  ;
			
output_statement:	  
	  OUTPUT expression (COMA expression)*
	  ->
	  	^(OUTPUTSTATEMENT expression+)
	;
	
expression:
	  boolean_expression
	  -> 
	  	^(EXPRESSION boolean_expression)
	;

boolean_expression:
	  relational_expression (boolean_operator^ relational_expression)*
	;
	
relational_expression:
	  a1_expression (relational_operator^ a1_expression)?
	;

a1_expression:
	  a2_expression (additive_operator^ a2_expression)*
	;

a2_expression:
	  a3_expression (multiplicative_operator^ a3_expression)*
	;

a3_expression:
	  INTNUMBER
	| REALNUMBER
	| TRUE
	| FALSE
	| STRING
	| CHAR
	| NULL
	| extended_id (LSQUARE expression RSQUARE)? -> ^(VAREXPR extended_id ^(INDEX expression)?)
	| cast
	| call
	| NOT a3_expression -> ^(NOT a3_expression)
	;
	
cast:		
	(LSQUARE simple_type RSQUARE) LPAREN boolean_expression RPAREN
	->
	  ^(CAST boolean_expression simple_type)
	  
	| LPAREN boolean_expression RPAREN
	-> 
	  boolean_expression
	;

boolean_operator:
	  AND
	| OR
	;
	
relational_operator:
	  EQUAL
	| NOTEQ
  	| GR 
  	| GREQ
  	| LS 
  	| LEQ
	;

additive_operator:
	  PLUS
	| MINUS
	;

multiplicative_operator:
	  DIV 
	| MOD
	| MULT
	;

operator:
	  relational_operator
	| additive_operator
	| multiplicative_operator
	| boolean_operator
	;	
	
/*------------------------------------------------------------------
 * LEXER RULES
 *------------------------------------------------------------------*/

INTNUMBER	: ('1' .. '9') ('0' .. '9')* | '0';

REALNUMBER : INTNUMBER '.' ('0' .. '9')+;

ID 	: ('a' .. 'z' | 'A' .. 'Z') ( 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9')* ;

STRING 	:  '"' (~ '"')* '"';

WHITESPACE : ( '\t' | ' ' | '\r' | '\n' )+ { $channel = HIDDEN; } ;

COMMENT : '/*' ( options {greedy=false;} : . )* '*/' { $channel = HIDDEN; } ;

CHAR 	: '\'' ('a' .. 'z' | 'A' .. 'Z') '\'';

COLON 	: ':' ('=' {$type = ASSIGN;})?;

EQUAL 	: '=' ('>' {$type = ARROW;})?;

GR 	: '>' ('=' {$type= GREQ;})?;

LS 	: '<' ('=' {$type = LEQ;})?;
